//=============================================================================
// Build the directory tree for the given files.
//=============================================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <dirent.h>

#include "common.h"
#include "list.h"
#include "dirtree.h"
#include "tags.h"
#include "plugin.h"
#include "woofs_plugin.h"
#include "strfuncs.h"
#include "path.h"


fs_tree *woofs_construct_tree (char *, list *);
int make_tree_aux (char *, list *, tree_node *);
char *get_woofs_path (char *, list *, plugin_set *);

plugin_set *woofs_plugin_set = NULL;		// Init later.

//=============================================================================
// Construct the woofs file tree corresponding to the given directory.
//
// For each tagged file encountered, 
//=============================================================================
fs_tree *woofs_construct_tree (char *dirpath, list *filters)
{
  fs_tree *tree = NULL;

  if ((tree = new_tree ()) == NULL) return (NULL);
  
  // Initialize the root.
  tree->root = new_tree_node ("root", REAL_FLAG | DIR_FLAG, NULL);
  if (tree->root == NULL)
    {
      free_tree (tree);
      return (NULL);
    }
  // Recurse on the current directory.
  if (make_tree_aux (dirpath, filters, tree->root) < 0)
    {
      free_tree (tree);
      return (NULL);
    }
  return (tree);
}

//=============================================================================
// A recursive function for constructing the FS tree.  Given the path to a
// real directory, it constructs the FS subtree rooted at parent.
//
// Base case: There are no real directories in the given parent dir.
//=============================================================================
int make_tree_aux (char *realpath, list *filters, tree_node *parent)
{
  DIR *dhandle = NULL;
  struct dirent *entry_iter = NULL;

  dhandle = opendir (realpath);
  if (dhandle == NULL) return (-1);

  while ((entry_iter = readdir (dhandle)) != NULL)
    {
      char *entry_path = NULL;
      if (strcmp (entry_iter->d_name, ".") == 0 || strcmp (entry_iter->d_name, "..") == 0)
	continue;
      entry_path = strconcat (realpath, "/");
      entry_path = mstrconcat (entry_path, entry_iter->d_name);
      if (entry_path == NULL)
	{
	  closedir (dhandle);
	  return (-1);
	}
      // If it's a directory, create a child for parent, then recurse on it.
      if (path_isdir (entry_path))
	{
	  tree_node *child = tree_addchild (parent, entry_iter->d_name, DIR_FLAG | REAL_FLAG);
	  if (child == NULL)
	    {
	      free (entry_path);
              closedir (dhandle);
	      return (-1);
	    }
	  make_tree_aux (entry_path, filters, child);
	}

      // If it's a regular file, get its woofs path and insert it.
      // This is the base case.
      else if (path_isreg (entry_path))
	{
	  char *woofs_path = get_woofs_path (entry_path, filters, woofs_plugin_set);
	  fprintf (stderr, "woofs_path so far is %s\n", woofs_path);
//	  woofs_path = path_getrelative (woofs_path, realpath);
	  fprintf (stderr, "realpath is %s\n", realpath);
	  fprintf (stderr, "woofs_path final is %s\n", woofs_path);
	  if (woofs_path == NULL)
	    {
	      free (entry_path);
	      closedir (dhandle);
	      return (-1);
	    }
	  tree_insertpath (parent, woofs_path, 0);

          free (woofs_path);  
	}
      free (entry_path);
    }
  closedir (dhandle);
  return (0);
}

//=============================================================================
// Translate the absolute real path of the given file to the corresponding
// canonicalized woofs path, based on the given filters.
//
// Arguments:
//   real_file_path: 
//   filters:
//   pset:
//=============================================================================
char *get_woofs_path (char *real_file_path, list *filters, plugin_set *pset)
{
  // The tags from the given file.
  tag_set *tags = NULL;
  // The path to the file relative to the mount path.
  char *relative_file_path = NULL;
  char *filename = NULL;
  // For traversing the filter list.
  list_node *filter_iter = NULL;

  // Get the file path relative to the mounted directory.
  if ((relative_file_path = path_getrelative (real_file_path, source_path)) == NULL)
    return (NULL);
  if ((filename = path_head (relative_file_path)) == NULL)
    {
      free (relative_file_path);
      return (NULL);
    }
  relative_file_path = path_mtail (relative_file_path);
  if (relative_file_path == NULL)
    {
      free (filename);
      return (NULL);
    }
  fprintf (stderr, "Real file path is %s, source_path is %s\n", real_file_path, source_path);
  fprintf (stderr, "relative_file_path is %s\n", relative_file_path);
  if ((tags = extract_tags (real_file_path, pset)) == NULL)
    {
      free (relative_file_path);
      free (filename);
      return (NULL);
    }

  // Iterate over the filters, adding a directory per filter.
  filter_iter = list_get_iter (filters);
  while (list_iter_hasmore (filter_iter))
    {
      char *curfilt = filter_iter->data;
      char *tagval = tag_set_lookup (tags, curfilt);

      fprintf (stderr, "current filter = %s, current tagval = %s\n", curfilt, tagval);
      // If this file doesn't contain the given tag, stop here.
      if (tagval == NULL) break;

      // relative_file_path += relative_file_path + "/" + tagval.
      if (strcmp (relative_file_path, "")) 
	relative_file_path = mstrconcat (relative_file_path, "/");
      relative_file_path = mstrconcat (relative_file_path, tagval);
      free (tagval);
      if (relative_file_path == NULL)
	{
	  tag_set_free (tags);
	  return (NULL);
	}
      list_iter_advance (filter_iter);
    }

  tag_set_free (tags);
  relative_file_path = mstrconcat (relative_file_path, "/");
  relative_file_path = mstrconcat (relative_file_path, filename);
  free (filename);
  return (relative_file_path);
}


int main (int argc, char **argv)
{
  fs_tree *tr = NULL;
  list *filters = NULL;
  int index = 0;
  if (argc < 4)
    {
      fprintf (stderr, "Usage: test_woofs <plugin_dir> <src_dir> <mount_dir>\n");
      return (-1);
    }
  filters = new_list ();
  for (index = 4; index < argc; index++)
    {
      fprintf (stderr, "Adding %s to filters.\n", argv[index]);
      list_insert (filters, index - 4, strdup (argv[index]));
    }
  woofs_plugin_set = plugin_set_new ();
  plugin_scandir (woofs_plugin_set, argv[1]);
  source_path = strdup (argv[2]);
  mount_path = strdup (argv[3]);
  fprintf (stderr, "source_path = %s\n", source_path);
  fprintf (stderr, "mount_path = %s\n", mount_path);
  tr = woofs_construct_tree (source_path, filters);
  tree_print (tr->root);
  plugin_set_fini (woofs_plugin_set);
  return (0);
}
